package com.xyl.audio.recorder;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;

import com.xyl.audio.utils.Utils;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author xyl on 2019/7/15.
 */
public class RecordUtil {
    //单例1
    private volatile static RecordUtil instance;

    private RecordUtil() {
    }

    public static RecordUtil getInstance() {
        if (instance == null) {
            synchronized (RecordUtil.class) {
                if (instance == null) {
                    instance = new RecordUtil();
                }
            }
        }
        return instance;
    }

    //=======================AudioRecord Default Settings=======================

    /**
     * 声源
     */
    private static final int DEFAULT_AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
    /**
     * 以下三项为默认配置参数。Google Android文档明确表明只有以下3个参数是可以在所有设备上保证支持的。
     * 采样率
     */
    public static final int DEFAULT_SAMPLING_RATE = 44100;//模拟器仅支持从麦克风输入44.1kHz采样率
    /**
     * 声道数
     * AudioFormat.CHANNEL_IN_STEREO 双声道
     */
    public static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
    /**
     * 音频采样精度
     * 每次采样的位宽为 16bit
     */
    public static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    /**
     * 每秒传送的比特(bit)数。单位为 bps(Bit Per Second)，比特率越高，传送数据速度越快。
     * <p>
     * 采样率x采样大小x声道数
     * 每秒钟采样的大小=16bit(位宽) 2(双通道) 44100(每次采样的次数hz) = 1411200b=1411.2kbps
     */

    //==================================================================
    private int sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes;
    private static final int[] mSampleRates = new int[]{8000, 11025, 22050, 44100};
    private AudioRecord audioRecord;
    private boolean isRecording = false;
    private Thread recordingThread = null;
    private String pcmPath;

    private boolean isReallyRecord;//是否正在录音mic

    public boolean isReallyRecord() {
        return isReallyRecord;
    }

    public void setReallyRecord(boolean reallyRecord) {
        isReallyRecord = reallyRecord;
    }

    /**
     * pcm音量实时改变接口
     */
    public interface OnVolumeChangeListener {
        void onVolumeChanged(int readSize, double volume);
    }

    public OnVolumeChangeListener onVolumeChangeListener;

    /**
     * 开始录制
     */
    public void startRecording(String filePath) {
        if (isRecording) {
            return;
        }
        pcmPath = filePath;
        if (audioRecord == null) {
            audioRecord = initAudioRecord();
        }
        try {
            audioRecord.startRecording();
            isRecording = true;
            if (recordingThread != null && !recordingThread.isInterrupted() && recordingThread.isAlive()) {
                recordingThread.isInterrupted();
                recordingThread = null;
            }
            //开启子线程独处音频数据流到文件
            recordingThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    writeAudioDataToFile(true);
                }
            }, "AudioRecord Thread");
            recordingThread.start();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
    }

    /**
     * 将录制的数据写入到文件中
     *
     * @param append 是否追加
     */
    private void writeAudioDataToFile(boolean append) {
        String filePath = pcmPath;
        short mPCMBuffer[] = new short[bufferSizeInBytes / 2];
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(filePath, append);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        if (os == null) {
            return;
        }
        synchronized (this) {
            while (isRecording) {
                int readSize = audioRecord.read(mPCMBuffer, 0, bufferSizeInBytes / 2);
                if (isReallyRecord) {
                    calculateRealVolume(mPCMBuffer, readSize);
                    try {
                        // writes the data to file from buffer stores the voice buffer
                        byte bData[] = Utils.short2byte(mPCMBuffer);
                        os.write(bData, 0, bufferSizeInBytes);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } else {
                    byte[] bytes = new byte[mPCMBuffer.length * 2];
                    try {
                        os.write(bytes, 0, bufferSizeInBytes);
                        if (onVolumeChangeListener != null) {
                            onVolumeChangeListener.onVolumeChanged(bytes.length, 0);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 计算pcm音频的音量
     *
     * @param mPCMBuffer
     * @param readSize
     */
    private void calculateRealVolume(short[] mPCMBuffer, int readSize) {
        long v = 0;
        // 将 buffer 内容取出，进行平方和运算
        for (int i = 0; i < mPCMBuffer.length; i++) {
            v += mPCMBuffer[i] * mPCMBuffer[i];
        }
        // 平方和除以数据总长度，得到音量大小。
        double mean = v / (double) readSize;
        double volume = 10 * Math.log10(mean);
        if (onVolumeChangeListener != null) {
            onVolumeChangeListener.onVolumeChanged(mPCMBuffer.length * 2, volume);
        }
    }

    /**
     * 停止录制
     */
    public void stopRecording() {
        if (recordingThread != null && recordingThread.isAlive() && !recordingThread.isInterrupted()) {
            recordingThread.interrupt();
            recordingThread = null;
        }
        if (null != audioRecord) {
            isRecording = false;
            audioRecord.stop();
            audioRecord.release();
            audioRecord = null;
        }
    }

    /**
     * 初始化AudioRecord
     *
     * @return
     */
    private AudioRecord initAudioRecord() {
        int bufferSizeTemp = AudioRecord.getMinBufferSize(DEFAULT_SAMPLING_RATE, DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT);
        //判断是否正确的值
        if (bufferSizeTemp != AudioRecord.ERROR_BAD_VALUE) {
            AudioRecord recordTemp = new AudioRecord(DEFAULT_AUDIO_SOURCE, DEFAULT_SAMPLING_RATE, DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT, bufferSizeTemp);
            this.sampleRateInHz = 44100;
            this.channelConfig = AudioFormat.CHANNEL_IN_MONO;
            this.audioFormat = AudioFormat.ENCODING_PCM_16BIT;
            this.bufferSizeInBytes = bufferSizeTemp;
            return recordTemp;
        }
        for (int rate : mSampleRates) {
            for (short audioFormat : new short[]{AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT}) {
                for (short channelConfig : new short[]{AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO}) {
                    try {
                        int bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat);
                        if (bufferSize > 0 && bufferSize <= 256) {
                            bufferSize = 256;
                        } else if (bufferSize > 256 && bufferSize <= 512) {
                            bufferSize = 512;
                        } else if (bufferSize > 512 && bufferSize <= 1024) {
                            bufferSize = 1024;
                        } else if (bufferSize > 1024 && bufferSize <= 2048) {
                            bufferSize = 2048;
                        } else if (bufferSize > 2048 && bufferSize <= 4096) {
                            bufferSize = 4096;
                        } else if (bufferSize > 4096 && bufferSize <= 8192) {
                            bufferSize = 8192;
                        } else if (bufferSize > 8192 && bufferSize <= 16384) {
                            bufferSize = 16384;
                        } else {
                            bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat);
                        }

                        if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
                            // check if we can instantiate and have a success
                            AudioRecord recorder = new AudioRecord(DEFAULT_AUDIO_SOURCE, rate, channelConfig, audioFormat, bufferSize);

                            if (recorder.getState() == AudioRecord.STATE_INITIALIZED) {
                                this.sampleRateInHz = rate;
                                this.channelConfig = channelConfig;
                                this.audioFormat = audioFormat;
                                this.bufferSizeInBytes = bufferSize;
                                return recorder;
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

}
